package org.cary.utils;

import java.util.Date;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 任务引擎，在指定是时间或者立即执行特定的任务，所有执行的任务都在指定的线程池中。 如果在指定的时间执行特定任务时，线程池中没有空闲的线程，那么这个任务也将排队等待有空闲的线程。 这个任务引擎你可以看成是有线程管理的{@link Thread} 或者是{@line TimerTask}.
 * @author cary
 */
public class TaskEngine {

	private static TaskEngine instance = new TaskEngine();

	/**
	 * 返回一个任务引擎实例
	 * @return
	 */
	public static TaskEngine getInstance() {
		return instance;
	}

	private Timer timer;	// 定时器
	private ExecutorService executor; // 执行管理服务
	private Map<TimerTask, TimerTaskWrapper> wrappedTasks = new ConcurrentHashMap<TimerTask, TimerTaskWrapper>();
	private boolean daemon = true;

	/**
	 * 构造一个新的引擎
	 */
	private TaskEngine() {
		timer = new Timer("timer-openfire", daemon);
		executor = Executors.newCachedThreadPool(new ThreadFactory() {
			final AtomicInteger threadNumber = new AtomicInteger(1);

			public Thread newThread(Runnable runnable) {
				// 使用我们自己的线程命名方式
				Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, "pool-netmachine" + threadNumber.getAndIncrement(), 0);
				// 设置任务为守护进程
				thread.setDaemon(daemon);
				if (thread.getPriority() != Thread.NORM_PRIORITY) {
					thread.setPriority(Thread.NORM_PRIORITY);
				}
				return thread;
			}
		});
	}

	/**
	 * 提交一个实时执行的任务也是需要排队的
	 * @param task
	 * @return
	 */
	public Future<?> submit(Runnable task) {
		return executor.submit(task);
	}

	/**
	 * 延迟时间来执行特定的task
	 * @param task
	 * @param delay 延迟的时间,单位：毫秒
	 */
	public void schedule(TimerTask task, long delay) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, true);
		wrappedTasks.put(task, taskWrapper);
		timer.schedule(taskWrapper, delay);
	}

	/**
	 * 在指定时间执行任务
	 * @param task
	 * @param time
	 */
	public void schedule(TimerTask task, Date time) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, true);
		// wrappedTasks.put(task, taskWrapper);
		timer.schedule(taskWrapper, time);
	}

	/**
	 * 延迟一定时间后，固定延时执行循环执行任务，可以参看{@link Timer#schedule()}
	 * @param task
	 * @param delay
	 * @param period，循环任务间的间隔时间，以毫秒为单位
	 */
	public void schedule(TimerTask task, long delay, long period) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, false);
		wrappedTasks.put(task, taskWrapper);
		timer.schedule(taskWrapper, delay, period);
	}

	/**
	 * 在指定的时间执行第一次任务，以后循环执行
	 * @param task
	 * @param firstTime
	 * @param period
	 */
	public void schedule(TimerTask task, Date firstTime, long period) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, false);
		wrappedTasks.put(task, taskWrapper);
		timer.schedule(taskWrapper, firstTime, period);
	}

	/***
	 * 延迟一定时间后，以固定速率执行循环执行任务，用来执行绝对 时间敏感的重复执行活动，如每小时准点打钟报时等等。
	 * @param task
	 * @param delay
	 * @param period
	 */
	public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, false);
		wrappedTasks.put(task, taskWrapper);
		timer.scheduleAtFixedRate(taskWrapper, delay, period);
	}

	/**
	 * @param task
	 * @param firstTime 第一次执行时间
	 * @param period 固定速率间隔时间
	 */
	public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
		TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task, false);
		wrappedTasks.put(task, taskWrapper);
		timer.scheduleAtFixedRate(taskWrapper, firstTime, period);
	}

	/**
	 * 取消任务执行 {@link java.util.TimerTask#cancel()}
	 * @param task 需要取消的任务.
	 */
	public void cancelScheduledTask(TimerTask task) {
		TaskEngine.TimerTaskWrapper taskWrapper = wrappedTasks.remove(task);
		if (taskWrapper != null) {
			taskWrapper.cancel();
		}
	}

	/**
	 * 关闭任务服务
	 */
	public void shutdown() {
		if (executor != null) {
			executor.shutdownNow();
			executor = null;
		}

		if (timer != null) {
			timer.cancel();
			timer = null;
		}
	}

	private class TimerTaskWrapper extends TimerTask {

		private TimerTask task;
		private boolean exeOnce;// 是否只执行一次

		public TimerTaskWrapper(TimerTask task, boolean exeOnce) {
			this.task = task;
			this.exeOnce = exeOnce;
		}

		public void run() {
			executor.submit(task);
			if (exeOnce) {
				wrappedTasks.remove(task);
			}
		}
	}
}
